package com.zz.ecommerce.utils;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Map与bean互转工具类,默认map key值首字母大写，bean 字段首字母小写
 * by zhangmeng
 */
public class BeanMapUtill {

    private final static Logger logger = LoggerFactory.getLogger(BeanMapUtill.class);

    /**
     * bean2map
     *
     * @param objBean
     * @param <T>
     * @return
     */
    public static <T> Map<String, Object> bean2Map(T objBean) {
        Map<String, Object> rtnMap = new HashMap<String, Object>();
        Field fields[] = objBean.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                Object value = field.get(objBean);
                if (null != value) {
                    rtnMap.put(ZzStringUtil.capFirst(field.getName()), value);
                }
            } catch (IllegalAccessException e) {
                logger.error("", e);
            }
            field.setAccessible(false);

        }
        return rtnMap;
    }


    /**
     * map2bean
     *
     * @param map
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T map2Bean(Map<String, Object> map, Class clazz, boolean caseSense)
        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Map<String, Object> keyMap = new HashMap<String, Object>();
        if (caseSense) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                keyMap.put(entry.getKey().toLowerCase(), entry.getValue());
            }
        } else {
            keyMap = map;
        }
        Constructor constructor = clazz.getConstructor(null);
        T bean = (T) constructor.newInstance();
        Field fields[] = clazz.getDeclaredFields();
        if (!CollectionUtils.isEmpty(map)) {
            for (Field field : fields) {
                field.setAccessible(true);
                String xmlNodeName = ZzStringUtil.capFirst(field.getName());
                if (caseSense) {
                    xmlNodeName = xmlNodeName.toLowerCase();
                }
                if (keyMap.containsKey(xmlNodeName) && !"serialVersionUID".equalsIgnoreCase(field.getName())) {
                    field.set(bean, keyMap.get(xmlNodeName));
                }
                field.setAccessible(false);
            }
        }
        return bean;
    }

    /**
     * mapList2BeanList
     *
     * @param mapList
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> mapList2BeanList(List<Map<String, Object>> mapList, Class clazz)
        throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        List<T> rtnList = new ArrayList<T>();
        for (Map<String, Object> map : mapList) {
            rtnList.add((T) map2Bean(map, clazz, true));
        }
        return rtnList;
    }

    /**
     * map2Bean
     *
     * @param map
     * @param clazz
     * @param levelKey
     * @param <T>
     * @return
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws InstantiationException
     */
    public static <T> T getMapBean(Map<String, Object> map, Class clazz, String levelKey)
        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Object lvValue = getLevelValue(map, levelKey);
        if (null != lvValue && lvValue instanceof Map) {
            return map2Bean((Map<String, Object>) lvValue, clazz, true);
        }
        return null;
    }

    /**
     * getMapBeanList
     *
     * @param map
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> getMapBeanList(Map<String, Object> map, Class clazz, String levelKey)
        throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        List<T> rtnList = new ArrayList<T>();
        Object lvValue = getLevelValue(map, levelKey);
        if ((null != lvValue)) {
            if (lvValue instanceof List) {
                rtnList.addAll((List<T>) mapList2BeanList((List<Map<String, Object>>) lvValue, clazz));
            } else if (lvValue instanceof Map) {
                rtnList.add((T) map2Bean((Map<String, Object>) lvValue, clazz, true));
            }
        }
        return rtnList;
    }

    /**
     * 从map中获得指定的对象，key值为用.分隔的层级
     *
     * @return
     */
    public static Object getLevelValue(Map<String, Object> map, String levelKey) {
        Object rtn = null;
        if (!CollectionUtils.isEmpty(map) && StringUtils.isNotEmpty(levelKey)) {
            String[] keys = levelKey.split("\\.");
            Object tmpMap = map;
            for (String key : keys) {
                if (null != tmpMap && tmpMap instanceof Map) {
                    tmpMap = ((Map) tmpMap).get(key);
                }
            }
            rtn = tmpMap;
        }
        return rtn;
    }

    /**
     * 设置list内容的对象的指定字段为指定值
     *
     * @param list
     * @param fieldName
     * @param value
     */
    public static <T> void setFieldValue(List<T> list, Class clazz, String fieldName, Object value)
        throws IllegalAccessException {
        Field field = getFieldByName(clazz, fieldName);
        if (null != list && null != field) {
            field.setAccessible(true);
            for (T o : list) {
                setField(o, field, value);
            }
            field.setAccessible(false);
        }

    }

    /**
     * @param bean
     * @param field
     * @param value
     * @param <T>
     * @throws IllegalAccessException
     */
    private static <T> void setField(T bean, Field field, Object value) throws IllegalAccessException {
        if (null != value) {
            Object setValue = convertValue(field.getType(), value);
            field.set(bean, setValue);
        }
    }

    /**
     * 类型转换,目前仅处理BigDecimal
     *
     * @param type
     * @param value
     * @return
     * @TODO:完善类型转换代码
     */
    private static Object convertValue(Class<?> type, Object value) {
        Object covertValue = null;
        if (null == value || type == value.getClass()) {
            covertValue = value;
        } else if (type == BigDecimal.class && StringUtils.isNumeric(String.valueOf(value))) {
            covertValue = new BigDecimal(String.valueOf(value));
        }
        return covertValue;
    }

    /**
     * 获取指定class的指定名字的field
     *
     * @param clazz
     * @param fieldName
     * @return
     */
    public static Field getFieldByName(Class clazz, String fieldName) {
        Field field = null;
        Field fields[] = clazz.getDeclaredFields();
        for (Field f : fields) {
            if (f.getName().equals(fieldName)) {
                field = f;
                break;
            }
        }
        return field;
    }

    /**
     * 设置对象的指定字段为指定值
     *
     * @param t
     * @param fieldName
     * @param value
     */
    public static <T> void setFieldValue(T t, String fieldName, Object value) throws IllegalAccessException {
        Field field = getFieldByName(t.getClass(), fieldName);
        field.setAccessible(true);
        setField(t, field, value);
        field.setAccessible(false);
    }

    public static void populate(Map<String, String> mapping, Object src, Object dest)
        throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Iterator<Map.Entry<String, String>> itr = mapping.entrySet().iterator();
        while (itr.hasNext()) {
            Map.Entry<String, String> entry = itr.next();
            String destProperty = entry.getKey();
            String srcProperty = entry.getValue();

            if (StringUtils.isEmpty(srcProperty)) {
                continue;
            }

            String value = BeanUtils.getProperty(src, srcProperty);
            if (value != null) {
                BeanUtils.setProperty(dest, destProperty, value);
            }
        }

    }

    public static <T, E> E swap(T from, E to, Map<String, String> mapper) {
        try {
            if (null == mapper) {
                return swap(from, to);
            }

            Map<String, String> fromMap = BeanUtils.describe(from);
            Map<String, String> toMap = BeanUtils.describe(to);

            for (String key : mapper.keySet()) {
                toMap.put(mapper.get(key), fromMap.get(key));
            }

            BeanUtils.populate(to, toMap);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return to;
    }

    public static <T, E> E swap(T from, E to) {
        try {
            Map<String, String> fromMap = BeanUtils.describe(from);
            BeanUtils.populate(to, fromMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return to;
    }

    public static <T, E> E swap(T from, Class clazz) {
        E to = null;
        try {
            Constructor constructor = clazz.getConstructor();
            to = (E) constructor.newInstance();
            swap(from, to);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return to;
    }

    public static <T, E> E swap(T from, Class clazz, Map<String, String> mapper) {
        E to = null;
        try {
            if (null == mapper) {
                return swap(from, clazz);
            }

            Constructor constructor = clazz.getConstructor();
            to = (E) constructor.newInstance();
            swap(from, to, mapper);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return to;
    }
}
